package com.urbainski.componentes.form
{
	import flash.events.Event;
	import flash.utils.getQualifiedClassName;
	
	import mx.core.EventPriority;
	import mx.core.UIComponent;
	import mx.utils.OnDemandEventDispatcher;
	
	import spark.components.Button;
	import spark.components.CheckBox;
	import spark.components.ComboBox;
	import spark.components.Label;
	import spark.components.List;
	import spark.components.NumericStepper;
	import spark.components.RichText;
	import spark.components.TextArea;
	import spark.components.TextInput;
	import spark.events.IndexChangeEvent;
	import spark.events.TextOperationEvent;
	
	/**
	 * @author Cristian Elder Urbainski
	 * @since 20/12/2011
	 */
	
	//--------------------------------------------------------------------------
	//
	//  Events
	//
	//--------------------------------------------------------------------------
	
	/**
	 *  ControlsHelper dispatches <code>propertyChange</code> event
	 *  when user makes any changes in the connected control.
	 * 
	 *  @eventType mx.events.PropertyChangeEvent.PROPERTY_CHANGE
	 * 
	 */
	[Event(name="propertyChange", type="mx.events.PropertyChangeEvent")]
	
	/**
	 *  DataFormItem uses this class for integration with a control.
	 * 
	 *  <p>ControlsHelper class updates <code>targetPropery</code> of a control
	 *  from <code>value</code> which comes from DataFormItem
	 *  and listens any changes made by user.
	 *  </p> 
	 *  <p>This helper class containts all mappings between <code>valueType</code>
	 *  and controls which should be created.
	 *  </p>
	 */
	public class ControlsHelper extends OnDemandEventDispatcher
	{
		//----------------------------------------------------------------------
		//
		//  Constructor
		//
		//----------------------------------------------------------------------
		
		public function ControlsHelper()
		{
			super();
		}
		
		//----------------------------------------------------------------------
		//
		//  Variables
		//
		//----------------------------------------------------------------------
		
		//----------------------------------
		//  eventType
		//----------------------------------
		
		/**
		 *  @private
		 *  Storage for the eventType property.
		 */
		private var _eventType:String;
		
		/**
		 *  
		 *  Write some comment ...
		 *
		 *  @default null
		 * 
		 */
		public function get eventType():String
		{
			return _eventType;
		}
		
		/**
		 *  @private
		 */
		public function set eventType(val:String):void
		{
			if (_eventType == val) return;
			
			/*if (target && _eventType)
				target.removeEventListener(_eventType, target_changeHandler);
			*/
			_eventType = val;
			
		/*	if (target && _eventType)
				target.addEventListener(_eventType, target_changeHandler, false, 0, true);*/
		}
		
		//----------------------------------------------------------------------
		//
		//  Properties
		//
		//----------------------------------------------------------------------
		
		//----------------------------------
		//  target
		//----------------------------------
		
		/**
		 *  @private
		 *  Storage for the target property.
		 */
		private var _target: UIComponent;
		
		public function get target(): UIComponent {
			return _target;
		}
		
		public function set target(val: UIComponent):void {
			if (_target == val) return;
			
			/*if (_target && eventType)
				_target.removeEventListener(eventType, target_changeHandler);
		*/	
			_target = val;
			
			/*if (_target && eventType)
				_target.addEventListener(eventType, target_changeHandler, false, 0, true);*/
		}
		
		
		//----------------------------------
		//  connected
		//----------------------------------
		
		/**
		 *  @private
		 *  Storage for the connected property.
		 */
		public var connected:Boolean;
		
		//----------------------------------
		//  value
		//----------------------------------
		
		/**
		 *  @private
		 *  Storage for the value property.
		 */
		//private var _value:Object;
		
		//[Inspectable(category="General", defaultValue="null")]
		
		/**
		 *  Write some comment ...
		 *  
		 *
		 *  @default null
		 *  
		 */
		/*public function get value():Object
		{
			return _value;
		}*/
		
		/**
		 *  @private
		 */
		/*public function set value(val:Object):void
		{
			if (_value == val) return;
			
			_value = val;
			
			// Push a new value into the control.
			setValueToTarget(_value);
		}*/
		
		//----------------------------------
		//  valueType
		//----------------------------------
		
		/**
		 *  @private
		 *  Storage for the valueType property.
		 */
		private var _valueType:String;
		
		[Inspectable(category="Data", enumeration="none,boolean,string,number,sequence,date,datetime,phone,ssn,currency,zip,email")]
		
		/**
		 *  
		 *  Write some comment ...
		 *
		 *  @default undefined
		 * 
		 */
		public function get valueType():String
		{
			return _valueType;
		}
		
		/**
		 *  @private
		 */
		public function set valueType(val:String):void
		{
			if (val != "boolean" && val != "string" && val != "number" &&
				val != "sequence" && val != "date" && val != "datetime" &&
				val != "phone" && val != "ssn" && val != "currency" &&
				val != "zip" && val != "email" && val != "none")
				throw new Error("ValueType '" + val + "' is not supported.");
			
			if (_valueType == val) return;
			
			_valueType = val;
			
			applyValueTypeOnTarget(val);
		}
		
		//----------------------------------
		//  targetProperty
		//----------------------------------
		
		/**
		 *  @private
		 *  Storage for the targetProperty property.
		 */
		private var _targetProperty:String;
		
		/**
		 *  
		 *  Write some comment ...
		 *
		 *  @default null
		 * 
		 */
		public function get targetProperty():String
		{
			return _targetProperty;
		}
		
		/**
		 *  @private
		 */
		public function set targetProperty(val:String):void
		{
			if (_targetProperty == val) return;
			
			_targetProperty = val;
			
			// Push a new value into the control.
			//setValueToTarget(value);
		}
		
		//----------------------------------------------------------------------
		//
		//  Methods
		//
		//----------------------------------------------------------------------
		
		/**
		 *  TODO : Write a comment ...
		 */
		public function connectControl(target:UIComponent):void
		{
			if (target == null)
				throw new Error("Control does not exist.");
			
			this.target = target;
			
			if (null != eventType) {
				// Fix by VS: disconnect from any "change" events during initialization
				// while this overwrites "value" in DataFormItem with garbage (like initial blank String in TextInput)
				//target.removeEventListener(eventType, target_changeHandler, false);
			}
			
			// If targetProperty was not specified by a developer, try to define it.
			if (targetProperty == null)
				targetProperty = getTargetPropertyByControl(target);
			
			// Define the type of the change event for target.
			if (eventType == null) {
				// Fix by VS: this will reassign handler if necessary
				eventType = getEventTypeByControl(target);
			} else {
				// Fix by VS: otherwise restore event handler explicitly
				target.addEventListener(eventType, target_changeHandler, false, EventPriority.DEFAULT, true);
			}
			
			connected = true;
		}
		
		/**
		 *  TODO : Write a comment ...
		 */
		public function releaseControl():void
		{
			if (!target) return;
			
			// Remove event listeners from the target component.
			/*if (eventType && target)
				target.removeEventListener(eventType, target_changeHandler, false);
			*/
			connected = false;
		}
		
		/**
		 *  TODO : Write a comment ...
		 */
		public function getEventTypeByControl(target:UIComponent):String
		{
			if (target == null)
				throw new Error("Control does not exist.");
			
			for (var i:int = 0; i < controlsConfig.length; i++) {
				var o:Object = controlsConfig[i];
				if (target is o.targetClass)
					return o.eventType;
			}
			
			//return null;
			
			throw new Error("EventType for class - " + flash.utils.getQualifiedClassName(target) + " was not defined.");
		}
		
		/**
		 *  TODO : Write a comment ...
		 */
		public function getTargetPropertyByControl(target:UIComponent):String
		{
			if (target == null)
				throw new Error("Control does not exist.");
			
			for (var i:int = 0; i < controlsConfig.length; i++) {
				var o:Object = controlsConfig[i];
				if (target is o.targetClass)
					return o.targetProperty;
			}
			
			//return null;
			
			throw new Error("TargetProperty for class - " + flash.utils.getQualifiedClassName(target) + " was not defined.");
		}
		
		private var controlsConfig:Array = initControlsConfig();
		
		protected function initControlsConfig():Array {
			var result:Array = [];
			
			//--------------------------
			//  CLEAR Controls
			//--------------------------
			/*result.push({
				targetClass:clear.components.NumericInput,
				targetProperty:"value",
				eventType:Event.CHANGE
				//eventType:FlexEvent.VALUE_COMMIT
			});
			
			result.push({
				targetClass:clear.components.CheckBox,
				targetProperty:"value",
				eventType:FlexEvent.VALUE_COMMIT
			});
			
			result.push({
				targetClass:clear.components.ComboBox,
				targetProperty:"value",
				eventType:IndexChangeEvent.CHANGE
			});*/
			
			/*		result.push({
			targetClass:clear.components.RadioButtonBarByKey,
			targetProperty:"value",
			eventType:Event.CHANGE
			//eventType:FlexEvent.VALUE_COMMIT
			});
			
			result.push({
			targetClass:clear.components.DropDownList,
			targetProperty:"value",
			eventType:Event.CHANGE
			//eventType:FlexEvent.VALUE_COMMIT
			});
			result.push({
			targetClass:clear.components.List,
			targetProperty:"value",
			eventType:IndexChangeEvent.CHANGE
			});*/
			
			//--------------------------
			//  SPARK Controls
			//--------------------------
			
			result.push({
				targetClass:spark.components.Label,
				targetProperty:"text",
				// Temporary use this type of event,
				// because Label control does not dispatch any events
				// if text property was changed.
				eventType:null
			});
			
			result.push({
				targetClass:spark.components.RichText,
				targetProperty:"text",
				// Temporary use this type of event,
				// because RichText control does not dispatch any events
				// if text property was changed.
				eventType:null
			});
			
			result.push({
				targetClass:spark.components.TextInput,
				targetProperty:"text",
				eventType:TextOperationEvent.CHANGE
				//eventType:FlexEvent.VALUE_COMMIT
			});
			
			result.push({
				targetClass:spark.components.TextArea,
				targetProperty:"text",
				eventType:TextOperationEvent.CHANGE
				//eventType:FlexEvent.VALUE_COMMIT
			});
			
			result.push({
				targetClass:spark.components.NumericStepper,
				targetProperty:"value",
				//eventType:FlexEvent.VALUE_COMMIT
				eventType:Event.CHANGE
			});
			
			result.push({
				targetClass:spark.components.CheckBox,
				targetProperty:"selected",
				eventType: Event.CHANGE
				//eventType:FlexEvent.VALUE_COMMIT
			});
			
			result.push({
				targetClass:spark.components.ComboBox,
				targetProperty:"selectedItem",
				eventType:IndexChangeEvent.CHANGE
			});
			
			result.push({
				targetClass:spark.components.List,
				targetProperty:"selectedItem",
				eventType:IndexChangeEvent.CHANGE
			});
			
			result.push({
				targetClass:spark.components.Button,
				targetProperty:null,
				eventType:null
			});
			
			//--------------------------
			//  MX Controls
			//--------------------------
			
			return result;
		}
		
		/**
		 *  TODO : Write a comment ...
		 */
		protected function setValueToTarget(val:Object):void
		{
			if (targetProperty == null || target == null) return;
			
			if (target.hasOwnProperty(targetProperty))
			{
				target[targetProperty] = val;
			}
			else
			{
				var errorMessage:String = "Property " + targetProperty + " was not found in " +
					flash.utils.getQualifiedClassName(target) + " class.";
				throw new Error(errorMessage);
			}
		}
		
		/**
		 *  TODO : Write a comment ...
		 */
		protected function getValueFromTarget():Object
		{
			if (targetProperty == null || target == null) return null;
			
			if (target.hasOwnProperty(targetProperty))
			{
				return target[targetProperty];
			}
			else
			{
				var errorMessage:String = "Property " + targetProperty + " was not found in " +
					flash.utils.getQualifiedClassName(target) + " class.";
				throw new Error(errorMessage);
			}
		}
		
		/**
		 *  TODO : Write a comment ...
		 */
		protected function applyValueTypeOnTarget(val:String):void
		{
			if (target == null) return;
			
			if (target.hasOwnProperty("valueType"))
				target["valueType"] = val;
		}
		
		/**
		 *  TODO : Write a comment ...
		 */
		/*protected function applyReadOnlyOnTarget(val:Boolean):void
		{
			if (target == null) return;
			
			if (target.hasOwnProperty("readOnly"))
			{
				target["readOnly"] = val;
			}
			else if (target.hasOwnProperty("editable") && !(target is clear.components.ComboBox)
				&& !(target is mx.controls.ComboBox))
			{ // clear.components.ComboBox and mx.controls.ComboBox have different meaning for editable
				target["editable"] = !val;
			}
			else if (target.hasOwnProperty("enabled"))
			{
				target["enabled"] = !val;
			}
		}*/
		
		
		//----------------------------------------------------------------------
		//
		//  Handlers
		//
		//----------------------------------------------------------------------
		
		/**
		 *  TODO : Write a comment ...
		 */
		protected function target_changeHandler(event:Event):void
		{
			// We don't need to dispatch any PropetyChange events
			// if this control should be not editable.
			
			//var oldValue:Object = value;
		/*	var newValue:Object = getValueFromTarget();
			
			//if (oldValue == newValue) return;
			
			var changeEvent:PropertyChangeEvent;
			changeEvent = PropertyChangeEvent.createUpdateEvent(target,
				targetProperty,
				newValue,
				newValue);
			//_value = newValue;
			dispatchEvent(changeEvent);*/
		}
		
	}
}
